#include "Ogre.h"
#include "QuadGridMesh.h"

using namespace Ogre;

QuadGridMesh::QuadGridMesh(unsigned int dim)
{
	dimension = 0;
	// assuming dim is power of 2.
	// restrict the maximum dimension to 128 to maintain 16bit index
	if (dim > 128)
		return;

	dimension = dim;

	create();
}

void QuadGridMesh::create()
{
	if (dimension == 0)
		return;

	const size_t width = dimension + 1;
	const size_t nVertices = width * width;

	VertexData* vData = new VertexData();
	vData->vertexCount = nVertices;

	vData->vertexDeclaration->addElement(0, 0, VET_FLOAT2, VES_POSITION);

	HardwareBufferManager& hbu = HardwareBufferManager::getSingleton();
	HardwareVertexBufferSharedPtr vbuf = hbu.createVertexBuffer(
		VertexElement::getTypeSize(VET_FLOAT2), nVertices, HardwareBuffer::HBU_STATIC_WRITE_ONLY);

	float *buf = (float *)vbuf->lock(HardwareBuffer::HBL_DISCARD);
	const float divider = (float)dimension;
	for(size_t y=0; y<=dimension; y++)
	{
		float yoffset = y/divider - 0.5f;
		for(size_t x=0; x<=dimension; x++)
		{
			float xoffset = x / divider - 0.5f;
			*buf++ = xoffset;
			*buf++ = yoffset;
		}
	}
	vbuf->unlock();

	size_t indexCount = dimension * dimension * 2 * 3;

	HardwareIndexBufferSharedPtr indexBuffer = HardwareBufferManager::getSingleton().createIndexBuffer(
			HardwareIndexBuffer::IT_16BIT, 
			indexCount, 
			HardwareBuffer::HBU_STATIC_WRITE_ONLY);

	unsigned short * pIbuf = (unsigned short *)indexBuffer->lock(HardwareBuffer::HBL_DISCARD);

	// Top Left Quad
	const unsigned short halfDim = dimension>>1;
	for(unsigned short y=0; y<halfDim; y++)
	{
		unsigned short curlineStart = y * (dimension+1);
		unsigned short nextlineStart = (y+1) * (dimension+1);
		unsigned short LT = curlineStart;
		unsigned short RT = LT + 1;
		unsigned short BL = nextlineStart;
		unsigned short BR = BL + 1;
		for(unsigned short x=0; x<halfDim; x++)
		{
			*pIbuf++ = LT;
			*pIbuf++ = BL;
			*pIbuf++ = RT;

			*pIbuf++ = RT;
			*pIbuf++ = BL;
			*pIbuf++ = BR;

			LT++; RT++; BL++; BR++;
		}
	}

	// Top Right Quad
	for(unsigned short y=0; y<halfDim; y++)
	{
		unsigned short curlineStart = y * (dimension+1) + halfDim;
		unsigned short nextlineStart = (y+1) * (dimension+1) + halfDim;
		unsigned short LT = curlineStart;
		unsigned short RT = LT + 1;
		unsigned short BL = nextlineStart;
		unsigned short BR = BL + 1;
		for(unsigned short x=0; x<halfDim; x++)
		{
			*pIbuf++ = LT;
			*pIbuf++ = BL;
			*pIbuf++ = RT;

			*pIbuf++ = RT;
			*pIbuf++ = BL;
			*pIbuf++ = BR;

			LT++; RT++; BL++; BR++;
		}
	}

	// Bottom Left Quad
	for(unsigned short y=halfDim; y<dimension; y++)
	{
		unsigned short curlineStart = y * (dimension+1);
		unsigned short nextlineStart = (y+1) * (dimension+1);
		unsigned short LT = curlineStart;
		unsigned short RT = LT + 1;
		unsigned short BL = nextlineStart;
		unsigned short BR = BL + 1;
		for(unsigned short x=0; x<halfDim; x++)
		{
			*pIbuf++ = LT;
			*pIbuf++ = BL;
			*pIbuf++ = RT;

			*pIbuf++ = RT;
			*pIbuf++ = BL;
			*pIbuf++ = BR;

			LT++; RT++; BL++; BR++;
		}
	}

	// Bottom Right Quad
	for(unsigned short y=halfDim; y<dimension; y++)
	{
		unsigned short curlineStart = y * (dimension+1) + halfDim;
		unsigned short nextlineStart = (y+1) * (dimension+1) + halfDim;
		unsigned short LT = curlineStart;
		unsigned short RT = LT + 1;
		unsigned short BL = nextlineStart;
		unsigned short BR = BL + 1;
		for(unsigned short x=0; x<halfDim; x++)
		{
			*pIbuf++ = LT;
			*pIbuf++ = BL;
			*pIbuf++ = RT;

			*pIbuf++ = RT;
			*pIbuf++ = BL;
			*pIbuf++ = BR;

			LT++; RT++; BL++; BR++;
		}
	}

	indexCountTL = indexCountTR = indexCountBL = indexCountBR = halfDim * halfDim * 6;
	indexTR = indexCountTL;
	indexBL = indexCountTL * 2;
	indexBR = indexCountTL * 3;

	indexBuffer->unlock();

	// Generate sub meshes for all the cases, index 0 means this grid won't be rendered, so not prapared.
	// bit0 - left top, bit1 - right top, bit2 - left bottom, bit3 - right bottom
	for(int i=1; i<16; i++)
	{
		String meshName("QuadGridMesh" + StringConverter::toString(dimension) + StringConverter::toString(i));
		mesh[i] = MeshManager::getSingleton().createManual(meshName, "General");
	
		mesh[i]->sharedVertexData = vData;

		VertexBufferBinding* bind = mesh[i]->sharedVertexData->vertexBufferBinding; 
		bind->setBinding(0, vbuf);

		SubMesh* sub;

		switch(i)
		{
		case 1: // left top
			break;
		case 2: // right top
			break;
		case 3: // left top + right top
			break;
		case 4: // left bottom
			break;
		case 5: // left top + left bottom
			break;
		case 6: // right top + left bottom
			break;
		case 7: // left top + right top + left bottom
			break;
		case 8: // right bottom
			break;
		case 9: // left top + right bottom
			sub = mesh[i]->createSubMesh();
			sub->useSharedVertices = true;
			sub->indexData->indexBuffer = indexBuffer;
			sub->indexData->indexCount = indexCountTR;
			sub->indexData->indexStart = 0;

			sub = mesh[i]->createSubMesh();
			sub->useSharedVertices = true;
			sub->indexData->indexBuffer = indexBuffer;
			sub->indexData->indexCount = indexCountTR;
			sub->indexData->indexStart = indexCountTR * 3;
			break;
		case 10: // right top + right bottom
			break;
		case 11: // left top + right top + right bottom
			break;
		case 12: // left bottom + right bottom
			break;
		case 13: // left top + left bottom + right bottom
			break;
		case 14: // right top + left bottom + right bottom
			break;
		case 15: // all the subnodes are enabled
			sub = mesh[i]->createSubMesh();
			sub->useSharedVertices = true;
			sub->indexData->indexBuffer = indexBuffer;
			sub->indexData->indexCount = indexCount;
			sub->indexData->indexStart = 0;
			break;
		}

		float maxBound = 0.5f;
		float minBound = -0.5f;
		mesh[i]->_setBounds(AxisAlignedBox(minBound, minBound, minBound, maxBound, maxBound, maxBound));
		mesh[i]->_setBoundingSphereRadius(0.7071f);

		mesh[i]->load();
	}
}
